﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SilentVoice.Models;

namespace SilentVoice.Data
{
    public class UserManagement
    {
        public Account CreateAccount(Account account)
        {
            using (NotesContext context = new NotesContext())
            {
                Account addedAccount = context.Accounts.Add(account);
                context.SaveChanges();

                return addedAccount;
            }
        }

        public Account UpdateAccount(Account account)
        {
            using (NotesContext context = new NotesContext())
            {
                Account updatedAccount = context.Accounts.Attach(account);
                context.Entry(account).State = System.Data.EntityState.Modified;
                context.SaveChanges();
                //TODO: what happens if event did not exist?
                return updatedAccount;
            }
        }

        public Account GetAccount(int accountId)
        {
            using (NotesContext context = new NotesContext())
            {
                Account account = context.Accounts.SingleOrDefault(x => x.Id == accountId);

                return account;
            }
        }

        public Account DeleteAccount(Account account)
        {
            using (NotesContext context = new NotesContext())
            {
                Account accountToDelete = context.Accounts.Single(x => x.Id == account.Id);

                accountToDelete = context.Accounts.Remove(accountToDelete);
                context.SaveChanges();
                return accountToDelete;
            }
        }

        public ICollection<User> GetAccountUsers(Account account)
        {
            return GetAccountUsers(account.Id);
        }

        public ICollection<User> GetAccountUsers(int id)
        {
            using (NotesContext context = new NotesContext())
            {
                Account existingAccount = context.Accounts.SingleOrDefault(x => x.Id == id);

                if (existingAccount == null)
                {
                    throw new ArgumentException(String.Format("Account with id {0} does not exist", id), "account");
                }

                return existingAccount.Users;
            }
        }

        public ICollection<Group> GetAccountGroups(Account account)
        {
            using (NotesContext context = new NotesContext())
            {
                Account existingAccount = context.Accounts.SingleOrDefault(x => x.Id == account.Id);

                if (existingAccount == null)
                {
                    throw new ArgumentException(String.Format("Account with id {0} does not exist", account.Id), "account");
                }

                return existingAccount.Groups;
            }
        }

        public User CreateUser(User user)
        {
            using (NotesContext context = new NotesContext())
            {
                User addedUser = context.Users.Add(user);
                context.SaveChanges();

                return addedUser;
            }
        }

        public User UpdateUser(User user)
        {
            using (NotesContext context = new NotesContext())
            {
                User updatedUser = context.Users.Attach(user);
                context.Entry(user).State = System.Data.EntityState.Modified;
                context.SaveChanges();
                //TODO: what happens if event did not exist?
                return updatedUser;
            }
        }

        public User GetUser(int userId)
        {
            using (NotesContext context = new NotesContext())
            {
                User user = context.Users.SingleOrDefault(x => x.Id == userId);

                return user;
            }
        }

        public User DeleteUser(User user)
        {
            using (NotesContext context = new NotesContext())
            {
                User userToDelete = context.Users.Single(x => x.Id == user.Id);

                userToDelete = context.Users.Remove(userToDelete);
                context.SaveChanges();
                return userToDelete;
            }
        }

        public Group CreateGroup(Group group)
        {
            using (NotesContext context = new NotesContext())
            {
                Group addedGroup = context.Groups.Add(group);
                context.SaveChanges();

                return addedGroup;
            }
        }

        public Group UpdateGroup(Group group)
        {
            using (NotesContext context = new NotesContext())
            {
                Group updatedGroup = context.Groups.Attach(group);
                context.Entry(group).State = System.Data.EntityState.Modified;
                context.SaveChanges();
                //TODO: what happens if event did not exist?
                return updatedGroup;
            }
        }

        public Group GetGroup(int groupId)
        {
            using (NotesContext context = new NotesContext())
            {
                Group group = context.Groups.SingleOrDefault(x => x.Id == groupId);

                return group;
            }
        }

        public Group DeleteGroup(Group group)
        {
            using (NotesContext context = new NotesContext())
            {
                Group groupToDelete = context.Groups.Single(x => x.Id == group.Id);

                groupToDelete = context.Groups.Remove(groupToDelete);
                context.SaveChanges();
                return groupToDelete;
            }
        }

        public ICollection<User> GetGroupUsers(Group group)
        {
            using (NotesContext context = new NotesContext())
            {
                Group existingGroup = context.Groups.SingleOrDefault(x => x.Id == group.Id);

                if (existingGroup == null)
                {
                    throw new ArgumentException(String.Format("Group with id {0} does not exist", group.Id), "account");
                }

                return existingGroup.Users;
            }
        }

        public User Register(User user, string password)
        {
            SaltedHash saltHash = new SaltedHash(password);
            user.PasswordHash = saltHash.Hash;
            user.PasswordSalt = saltHash.Salt;

            return CreateUser(user);
        }

        public User Login(string username, string password)
        {
            using (NotesContext context = new NotesContext())
            {
                User matchedUser = context.Users.SingleOrDefault(x => x.Email.Equals(username));

                if (matchedUser == null)
                {
                    throw new Models.Exceptions.BadLogin();
                }

                string salt = matchedUser.PasswordSalt;
                string hash = matchedUser.PasswordHash;

                if (SaltedHash.Verify(salt, hash, password))
                {
                    return matchedUser;
                }

                throw new Models.Exceptions.BadLogin();
            }
        }

        //TODO: test this method
        public Role GetRole(User user)
        {
            using (NotesContext context = new NotesContext())
            {
                Role role = context.Roles.Single(x => x.Id == user.RoleId);

                return role;
            }
        }
    }
}
